{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 面向对象编程\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 类的封装\n",
    "python支持面向对象编程，我们要学习python中如何创建类，通过类创建对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.student'>\n",
      "<function student.hello at 0x000001E1D81A9E50>\n"
     ]
    }
   ],
   "source": [
    "#类的定义\n",
    "#定义一个学生类\n",
    "class student:\n",
    "    num=45\n",
    "    def hello(self):\n",
    "        return \"hahaahahah\"\n",
    "#python中类本身也是个对象\n",
    "print(student)\n",
    "#我们可以直接访问类的成员\n",
    "print(student.hello)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.student object at 0x000001E1D81CB5E0>\n",
      "Student('180430888','哈哈哈')\n",
      "180430888\n",
      "哈哈哈\n",
      "Student('180430888','哈哈哈')\n",
      "<class '__main__.Student'>\n",
      "1 1\n",
      "1 '1'\n",
      "1 <class 'str'>\n"
     ]
    }
   ],
   "source": [
    "#创建对象\n",
    "stu0 = student()\n",
    "print(stu0)\n",
    "#如何定义实例变量，如何定义构造方法\n",
    "#每个类只能定义一个构造方法 __init__\n",
    "class Student:\n",
    "    num=0\n",
    "    def __init__(self,stuno,name):\n",
    "        self.stuno = stuno\n",
    "        self.name = name\n",
    "        Student.num+=1\n",
    "        self.num=8888\n",
    "    def hello(self):\n",
    "        print(\"你好{}同学\".format(self.name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.stuno,self.name)\n",
    "\n",
    "#创建对象stu1时，会调用__init__构造方法，对类进行初始化\n",
    "stu1=Student('180430888',\"哈哈哈\")\n",
    "print(stu1)\n",
    "print(stu1.stuno)\n",
    "print(stu1.name)\n",
    "#__repr__特殊方法\n",
    "#在python里面，我们往往使用repr方法返回一个字符串，通过eval该字符串可以重建这个对象\n",
    "stu2 = eval(repr(stu1))\n",
    "print(stu2)\n",
    "print(type(stu2))\n",
    "#python的内置的类型中repr基本是这个原则，会发现repr会比print更能体现变量的类型，并且可以通过eval还原该值\n",
    "n = 1\n",
    "print(n,repr(n))\n",
    "n = '1'\n",
    "print(n,repr(n))\n",
    "n2 = eval(repr(n))\n",
    "print(n2,type(n2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好呵呵呵同学\n",
      "180430999\n",
      "呵呵呵\n",
      "你好杨俊凯同学\n",
      "000\n",
      "杨俊凯\n"
     ]
    }
   ],
   "source": [
    "#设置成员变量的值\n",
    "stu1.stuno='180430999'\n",
    "stu1.name='呵呵呵'\n",
    "print(stu1)\n",
    "print(stu1.stuno)\n",
    "print(stu1.name)\n",
    "stu2 = Student('000','杨俊凯')\n",
    "print(stu2)\n",
    "print(stu2.stuno)\n",
    "print(stu2.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8888\n",
      "8888\n",
      "2\n",
      "False\n",
      "2\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "#类变量属于类，所有对象共享一个变量，实例变量属于具体的对象，所以命名的时候尽量不要出现相同的变量名\n",
    "#类变量可以通过类名或对象名访问，实例变量只能通过对象名访问\n",
    "#如果通过对象名访问，默认是实例变量，如果没有才会访问类变量\n",
    "print(stu1.num)\n",
    "print(stu2.num)\n",
    "print(Student.num)\n",
    "print(stu1.num is Student.num)\n",
    "del stu1.num\n",
    "print(stu1.num)\n",
    "print(stu1.num is Student.num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18\n"
     ]
    }
   ],
   "source": [
    "#我们可以随时增加成员变量，python中很随意的，java能这么干么？\n",
    "stu1.age=18\n",
    "print(stu1.age)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18\n"
     ]
    }
   ],
   "source": [
    "#python中成员通过字典来访问\n",
    "print(stu1.__dict__['age'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好杨俊凯同学\n",
      "<class 'method'>\n",
      "<class 'function'>\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "info() missing 1 required positional argument: 'cls'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-9-675256d7498d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstu2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mhello\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mStudent\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mhello\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 8\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mStudent\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: info() missing 1 required positional argument: 'cls'"
     ]
    }
   ],
   "source": [
    "#可以通过对象调用方法，并没有输入self参数，本质上是为了区分方法和函数\n",
    "stu2.hello()\n",
    "#方法和函数的区别\n",
    "#绑定到对象上的函数就是方法\n",
    "print(type(stu2.hello))\n",
    "print(type(Student.hello))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "班级人数:2\n",
      "班级人数:2\n",
      "班级人数:2\n",
      "班级人数:2\n",
      "班级人数:2\n",
      "班级人数:2\n"
     ]
    }
   ],
   "source": [
    "#类方法和静态方法\n",
    "class Student:\n",
    "    num=0\n",
    "    def __init__(self,stuno,name):\n",
    "        self.stuno = stuno\n",
    "        self.name = name\n",
    "        Student.num+=1\n",
    "        self.num=8888\n",
    "    def hello(self):\n",
    "        print(\"你好{}同学\".format(self.name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.stuno,self.name)\n",
    "    #类方法,可以访问类成员\n",
    "    @classmethod\n",
    "    def info(cls):\n",
    "        print('班级人数:{}'.format(cls.num))\n",
    "        \n",
    "    #由于只有一个构造方法，可以通过@classmethod实现新的类方法来构造新的对象\n",
    "    def createStudent(cls,stuno,name,age):\n",
    "        stu = Student(stuno,name)\n",
    "        stu.age=age\n",
    "        return stu\n",
    "    \n",
    "    #静态方法，必须通过类名访问\n",
    "    @staticmethod\n",
    "    def info2():\n",
    "        print('班级人数:{}'.format(Student.num))\n",
    "        \n",
    "stu2 = Student('2','222')\n",
    "stu3 = Student('3','333')\n",
    "stu2.info()\n",
    "Student.info()\n",
    "stu3.info()\n",
    "stu2.info2()\n",
    "Student.info2()\n",
    "stu3.info2()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Student(3333,三三三三)\n",
      "三三三三\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute '__name'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m-----------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                        Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-17-18d9b8553f65>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     20\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstu3\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgetname\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     21\u001b[0m \u001b[1;31m#不能直接访问私有成员\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 22\u001b[1;33m \u001b[1;32massert\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstu3\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__name\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'Student' object has no attribute '__name'"
     ]
    }
   ],
   "source": [
    "#python的私有成员\n",
    "#在变量前加2个下划线表明这是个私有成员\n",
    "#可以通过get方法获取私有成员\n",
    "class Student:\n",
    "    num=0\n",
    "    def __init__(self,stuno,name):\n",
    "        self.__stuno = stuno\n",
    "        self.__name = name\n",
    "        Student.num+=1\n",
    "        self.num=8888\n",
    "    def __hello(self):\n",
    "        print(\"你好{}同学\".format(self.__name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.__stuno,self.__name)\n",
    "    def getname(self):\n",
    "        return self.__name\n",
    "    \n",
    "stu3 = Student('3333','三三三三')\n",
    "print(stu3)\n",
    "print(stu3.getname())\n",
    "#不能直接访问私有成员\n",
    "assert(stu3.__name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "中国加油\n",
      "{'_Student__stuno': '3333', '_Student__name': '中国加油', 'num': 8888}\n",
      "你好33333同学\n"
     ]
    }
   ],
   "source": [
    "#但是在python中没有什么事真正私有的,比如刚才的私有成员我们就可以改\n",
    "#到这儿大家应该可以看出，为什么不能访问私有，无非就是真实的名字改了而已\n",
    "stu3._Student__name='中国加油'\n",
    "print(stu3.getname())\n",
    "#我们会发现在字典属性里面也包含了私有成员\n",
    "print(stu3.__dict__)\n",
    "#通过__dict__访问私有成员\n",
    "stu3.__dict__['_Student__name']='33333'\n",
    "print(stu3)\n",
    "#为何私有成员这么不严格，可能是因为“大家都是成年人了”吧"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "haha\n",
      "Student(4,hehe)\n"
     ]
    }
   ],
   "source": [
    "#在python中类的成员只有两种可见性：公共和私有\n",
    "#如果要实现保护可以遵循一个规则就是以单下划线开头\n",
    "#对私有成员的访问，可以通过@property注解来设置属性对__stuno和__name进行操作\n",
    "class Student:\n",
    "    def __init__(self,stuno,name):\n",
    "        self.__stuno = stuno\n",
    "        self.__name = name\n",
    "    def __hello(self):\n",
    "        print(\"你好{}同学\".format(self.__name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.__stuno,self.__name)\n",
    "    #python中可以通过@property注解来把方法设置为属性来访问私有成员\n",
    "    #和java相比，结果如何\n",
    "    #使用property比定义getter setter更简洁，简单属性尽量使用property\n",
    "    @property\n",
    "    def name(self):\n",
    "        return self.__name\n",
    "    @name.setter\n",
    "    def name(self,value):\n",
    "        self.__name=value\n",
    "    \n",
    "stu4 = Student('4','haha')\n",
    "print(stu4.name)\n",
    "stu4.name='hehe'\n",
    "print(stu4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Student(5555,5555)\n",
      "Student(6666,6666)\n",
      "{Student(6666,6666), Student(7777,7777)}\n",
      "{Student(5555,5555), Student(7777,7777)}\n"
     ]
    }
   ],
   "source": [
    "#任务6.1 新建类Student\n",
    "#1. 类中有三个成员学号，姓名和朋友，都是私有成员，定义构造函数初始化他们\n",
    "#2. 定义方法makefriends添加朋友，参数是Student对象\n",
    "#3. 定义方法__repr__输出对象信息\n",
    "#4. 定义至少3个对象进行测试\n",
    "class Student:\n",
    "    def __init__(self,stuno,name):\n",
    "        self.__stuno = stuno\n",
    "        self.__name = name\n",
    "        self.friends = set()\n",
    "    def __hello(self):\n",
    "        print(\"学号:{}姓名:{}\".format(self.__stuno,self.__name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.__stuno,self.__name)\n",
    "    def makefriends(self,friend):\n",
    "        self.friends.add(friend)\n",
    "        friend.friends.add(self)\n",
    "\n",
    "stu5 = Student('5555','5555')\n",
    "stu6 = Student('6666','6666')\n",
    "stu7 = Student('7777','7777')\n",
    "stu5.makefriends(stu6)\n",
    "stu5.makefriends(stu7)\n",
    "stu6.makefriends(stu7)\n",
    "print(stu5)\n",
    "print(stu6)\n",
    "print(stu5.friends)\n",
    "print(stu6.friends)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "设置新属性_Student__stuno,默认值5\n",
      "设置新属性_Student__name,默认值呜呜\n",
      "通过__getattribute__访问成员\n",
      "通过__getattr__访问成员，不存在属性mingzi\n",
      "设置新属性mingzi,默认值哈哈\n",
      "哈哈\n",
      "通过__getattribute__访问成员\n",
      "哈哈\n",
      "通过__getattribute__访问成员\n",
      "通过__getattribute__访问成员\n",
      "Student(5,呜呜)\n"
     ]
    }
   ],
   "source": [
    "#python中灵活好还可以支持访问不存在的属性，在访问属性前后插入相应的处理，非常灵活,在需要对属性进行拦截处理的场合非常合适\n",
    "#主要的实现方法有  __getattr__,__getattribute,__setattr__\n",
    "class Student:\n",
    "    def __init__(self,stuno,name):\n",
    "        self.__stuno = stuno\n",
    "        self.__name = name\n",
    "    def __hello(self):\n",
    "        print(\"你好{}同学\".format(self.__name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.__stuno,self.__name)\n",
    "    @property\n",
    "    def name(self):\n",
    "        return self.__name\n",
    "    @name.setter\n",
    "    def name(self,value):\n",
    "        self.__name=value\n",
    "    def __getattr__(self,attrname):\n",
    "        print(\"通过__getattr__访问成员，不存在属性{}\".format(attrname))\n",
    "        setattr(self,attrname,\"哈哈\")\n",
    "        return \"哈哈\"\n",
    "    def __setattr__(self,attrname,value):\n",
    "        print(\"设置新属性{},默认值{}\".format(attrname,value))\n",
    "        #这句把属性设置到对象的__dict__里面\n",
    "        super().__setattr__(attrname,value)\n",
    "    def __getattribute__(self,attrname):\n",
    "        print(\"通过__getattribute__访问成员\")\n",
    "        return super().__getattribute__(attrname)\n",
    "stu8 = Student('5',\"呜呜\")\n",
    "print(stu8.mingzi)\n",
    "#第2次就不调用__getattr__了\n",
    "print(stu8.mingzi)\n",
    "print(stu8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['_Student__hello', '__add__', '__class__', '__contains__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__plus__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']\n",
      "Student:学生构造了\n",
      "Student:学生构造了\n",
      "True\n",
      "学号：6，姓名：666\n",
      "False\n",
      "Student:学生构造了\n",
      "学号：66，姓名：666666\n",
      "学号：6，姓名：666\n",
      "Student:学生构造了\n",
      "学号：66，姓名：666666\n"
     ]
    }
   ],
   "source": [
    "#python的类有很多内置方法，可以帮我们给类添加更多丰富的功能\n",
    "class Student:\n",
    "    def __init__(self,stuno,name):\n",
    "        self.__stuno = stuno\n",
    "        self.__name = name\n",
    "        print(\"Student:学生构造了\")\n",
    "    def __hello(self):\n",
    "        print(\"你好{}同学\".format(self.__name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.__stuno,self.__name)\n",
    "    #比较功能是先两个对象的比较 stu1==stu2\n",
    "    def __eq__(self,other):\n",
    "        return self.__stuno==other.__stuno and self.__name==other.__name\n",
    "    #实现获取字符串str(stu)\n",
    "    def __str__(self):\n",
    "        return \"学号：{}，姓名：{}\".format(self.__stuno,self.__name)\n",
    "    #实现运算符重载，比如<号\n",
    "    def __lt__(self,other):\n",
    "        return self.__stuno < other.__stuno\n",
    "    #实现加法+\n",
    "    def __add__(self,other):\n",
    "        return Student(self.__stuno+other.__stuno,self.__name+other.__name)\n",
    "    #实现+=\n",
    "    def __plus__(self,other):\n",
    "        self.__stuno += other.__stuno\n",
    "        self.__name += other.__name\n",
    "        return self\n",
    "    #下面的同学有兴趣可以自己去实现\n",
    "    #迭代器iter(stu)\n",
    "    def __iter__(self):pass\n",
    "    #长度len(stu)\n",
    "    def __len__(self):pass\n",
    "    #下一个next(stu)\n",
    "    def __next__(self):pass\n",
    "    # key in stu\n",
    "    def __contains__(self,key):pass\n",
    "    \n",
    "\n",
    "#查看所有内置方法\n",
    "print(dir(Student))\n",
    "#常用内置方法\n",
    "stu6 = Student('6','666')\n",
    "stu66 = Student('6','666')\n",
    "print(stu6==stu66)\n",
    "print(str(stu6))\n",
    "print(stu6<stu66)\n",
    "#合体了\n",
    "print(stu6+stu66)\n",
    "print(stu6)\n",
    "#真正合体\n",
    "stu6+=stu66\n",
    "print(stu6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#任务6.2 补充完整student类，实现 - ，-=， >,iter,len,next,in等，并展现测试效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 类的继承"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[<class '__main__.Z'>, <class '__main__.K3'>, <class '__main__.K2'>, <class '__main__.D'>, <class '__main__.K1'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class 'object'>]\n"
     ]
    }
   ],
   "source": [
    "#继承关系查看\n",
    "#本质上是广度优先遍历\n",
    "class A: pass\n",
    "class B: pass\n",
    "class C: pass\n",
    "class D: pass\n",
    "class E: pass\n",
    "class K1(A, B, C): pass\n",
    "class K3(D, A): pass\n",
    "class K2(D, B, E): pass\n",
    "class Z(K3, K2, K1): pass\n",
    "print(Z.mro())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "do somthing to cls:AtSchoolStudent\n"
     ]
    }
   ],
   "source": [
    "#继承的基本语法\n",
    "#定义一个学生类\n",
    "class Student:\n",
    "    def __init__(self,stuno,name):\n",
    "        self.stuno = stuno\n",
    "        self.name = name\n",
    "    def introduce(self):\n",
    "        print(\"我叫{}我的学号是{}\".format(self.stuno,self.name))\n",
    "    def __repr__(self):\n",
    "        return \"Student('{}','{}')\".format(self.stuno,self.name)\n",
    "    \n",
    "    # python3.6推出新的类方法classmethod object.__init_subclass__(cls) \n",
    "    # 当一个class B 继承自class A时，会调用class A的__init_subclass__方法，此时的cls指class B。\n",
    "    # 所以我们可以在__init__subclass__中做一些对子类创建时的干预，无论将来创建多少子类，都会被干预\n",
    "    # 如果你定义__init__subclass__为实例方法，也会被隐式转换为类方法\n",
    "    def __init_subclass__(cls):\n",
    "        super().__init_subclass__()\n",
    "        print(\"do somthing to cls:{}\".format(cls.__name__))\n",
    "\n",
    "#定义一个在校生类   \n",
    "class AtSchoolStudent(Student):pass\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#如果子类没有定义__init__则，自动会调用父类的__init__\n",
    "#所以如果写 stu = AtSchoolStudent()会出错\n",
    "stu8 = AtSchoolStudent()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫8888我的学号是88888\n",
      "[<class '__main__.AtSchoolStudent'>, <class '__main__.Student'>, <class 'object'>]\n"
     ]
    }
   ],
   "source": [
    "#直接使用基类的参数列表就不会出错了\n",
    "stu8= AtSchoolStudent('8888','88888')\n",
    "print(stu8)\n",
    "print(AtSchoolStudent.mro())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "do somthing to cls:GraduateStudent\n",
      "GraduateStudent：毕业生构造了\n",
      "Student('999','999')\n",
      "我叫999我有毕业证书\n",
      "[<class '__main__.GraduateStudent'>, <class '__main__.Student'>, <class 'object'>]\n"
     ]
    }
   ],
   "source": [
    "#定义一个毕业生\n",
    "class GraduateStudent(Student):\n",
    "    #定义了自己的构造方法\n",
    "    def __init__(self,stuno,name,salary):\n",
    "        #在自己的构造方法里面，调用父类的构造\n",
    "        super().__init__(stuno,name)\n",
    "        self.__salary=salary\n",
    "        print(\"GraduateStudent：毕业生构造了\")\n",
    "    \n",
    "    def introduce(self):\n",
    "        print(\"我叫{}我有毕业证书\".format(self.name))\n",
    "    \n",
    "stu9 = GraduateStudent('999','999',99999)\n",
    "print(stu9)\n",
    "stu9.introduce()\n",
    "#查看继承关系\n",
    "print(GraduateStudent.mro())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NotGraduateStudent:结业生构造了\n",
      "我叫101010我的学号是101010\n",
      "我叫101010我有结业证书\n"
     ]
    }
   ],
   "source": [
    "#定义一个结业生\n",
    "class NotGraduateStudent(Student):\n",
    "    def __init__(self,stuno,name,salary):\n",
    "        Student.__init__(self,stuno,name)\n",
    "        self.salary = salary\n",
    "        print(\"NotGraduateStudent:结业生构造了\")\n",
    "    def introduce(self):\n",
    "        print(\"我叫{}我有结业证书\".format(self.name))\n",
    "        \n",
    "    \n",
    "stu10 = NotGraduateStudent('101010','101010',10101010)\n",
    "print(stu10)\n",
    "stu10.introduce()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NotGraduateStudent:结业生构造了\n",
      "NotAtSchoolStudent：非在校生构造了\n",
      "我叫11我的学号是11\n",
      "我叫11我有结业证书\n"
     ]
    }
   ],
   "source": [
    "#定义一个非在校生NotAtSchoolStudent,继承GraduateStudent和NotGraduateStudent\n",
    "class NotAtSchoolStudent(NotGraduateStudent,GraduateStudent):\n",
    "    def __init__(self,stuno,name,salary):\n",
    "        super().__init__(stuno,name,salary)\n",
    "        print(\"NotAtSchoolStudent：非在校生构造了\")\n",
    "        \n",
    "    #def introduce(self):\n",
    "    #    print(\"我叫{}我已经不在学校了，从此是社会人了\".format(self.name))\n",
    "        \n",
    "stu11 = NotAtSchoolStudent('11','11',111111111)\n",
    "print(stu11)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫11我有结业证书\n"
     ]
    }
   ],
   "source": [
    "#通过注释每个类中的introduce来查看每个\n",
    "stu11.introduce()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 异常处理\n",
    "当程序运行时，经常会出现各种各样的错误，然后程序就自动退出了。那么如何编写更加健壮的程序，就需要学习异常处理的机制了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "HealthInfo(xh=111,xm='haha',ma='红码',bj='111',xy='rrrr',yy='在国外')\n",
      "HealthInfo(xh=111,xm='haha',ma='红码',bj='111',xy='rrrr',yy='在国外')\n",
      "<class '__main__.HealthInfo'>\n"
     ]
    }
   ],
   "source": [
    "#定义学生健康信息，包括学号，姓名，健康码（绿码、皇马、红马），班级，学院，原因等信息\n",
    "class HealthInfo:\n",
    "    def __init__(self,*args0,**args1):\n",
    "        self.xh = args1['xh']\n",
    "        self.xm = args1['xm']\n",
    "        self.ma = args1['ma']\n",
    "        self.bj = args1['bj']\n",
    "        self.xy = args1['xy']\n",
    "        self.yy = args1['yy']\n",
    "    def __repr__(self):\n",
    "        return \"HealthInfo(xh={},xm='{}',ma='{}',bj='{}',xy='{}',yy='{}')\".format(self.xh,self.xm,self.ma,self.bj,self.xy,self.yy)\n",
    "\n",
    "\n",
    "stu0 = HealthInfo(xh=111,xm='haha',ma='红码',bj='111',xy='rrrr',yy='在国外')\n",
    "print(stu0)\n",
    "print(repr(stu0))\n",
    "stu1 = eval(repr(stu0))\n",
    "print(type(stu1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入学号:afda\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "invalid literal for int() with base 10: 'afda'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-14-fa226d2e6aa4>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[0mstudents\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m     \u001b[0mxh\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0minput\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'请输入学号:'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      6\u001b[0m     \u001b[0mxm\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minput\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'请输入姓名:'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      7\u001b[0m     \u001b[0mma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0minput\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'请输入健康码:'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: invalid literal for int() with base 10: 'afda'"
     ]
    }
   ],
   "source": [
    "#任务6.3 编写程序，输入3个学生健康信息(学号是9位数字)放入列表，最后输出所有学生信息\n",
    "#这个程序学号输入字符时会出现异常，程序结束，无法达到输入3个学生的任务\n",
    "# 修改下列程序，加入异常处理功能，让程序稳定，语法 try\n",
    "students=[]\n",
    "for i in range(3):\n",
    "    xh = int(input('请输入学号:'))\n",
    "    xm = input('请输入姓名:')\n",
    "    ma = input('请输入健康码:')\n",
    "    bj = input('请输入班级:')\n",
    "    xy = input('请输入学院:')\n",
    "    yy = input('请输入原因:')\n",
    "    student.add(HealthInfo(xh=xh,xm=xm,ma=ma,xy=xy,yy=yy))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入学号:asd\n",
      "invalid literal for int() with base 10: 'asd'\n",
      "请输入学号:1\n",
      "请输入姓名:1\n",
      "请输入健康码:1\n",
      "请输入班级:1\n",
      "请输入学院:1\n",
      "请输入原因:1\n",
      "请输入学号:2\n",
      "请输入姓名:2\n",
      "请输入健康码:2\n",
      "请输入班级:2\n",
      "请输入学院:2\n",
      "请输入原因:2\n"
     ]
    }
   ],
   "source": [
    "students=[]\n",
    "for i in range(3):\n",
    "    try:\n",
    "        xh = int(input('请输入学号:'))\n",
    "        xm = input('请输入姓名:')\n",
    "        ma = input('请输入健康码:')\n",
    "        bj = input('请输入班级:')\n",
    "        xy = input('请输入学院:')\n",
    "        yy = input('请输入原因:')\n",
    "        students.append(HealthInfo(xh=xh,xm=xm,ma=ma,bj=bj,xy=xy,yy=yy))\n",
    "    except ValueError as e:\n",
    "        print(e)\n",
    "print(students)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#任务6.3.2 在上面的任务中，如果有异常，那么最终输入的人数会少一个，如何确保用户是输入3个人\n",
    "students=[]\n",
    "for i in range(3):\n",
    "    while True:\n",
    "        try:\n",
    "            xh = int(input('请输入学号:'))\n",
    "            xm = input('请输入姓名:')\n",
    "            ma = input('请输入健康码:')\n",
    "            bj = input('请输入班级:')\n",
    "            xy = input('请输入学院:')\n",
    "            yy = input('请输入原因:')\n",
    "            students.append(HealthInfo(xh=xh,xm=xm,ma=ma,bj=bj,xy=xy,yy=yy))\n",
    "            break\n",
    "        except ValueError as e:\n",
    "            print(e)\n",
    "print(students)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "学号11111111需要是9位数\n"
     ]
    }
   ],
   "source": [
    "#任务6.3.3改造HealthInfo中的init方法，如果发现xh不是9位数就抛出异常，告诉用户，学号不是9位数\n",
    "#写法是raise ValueError('')\n",
    "class HealthInfo:\n",
    "    def __init__(self,*args0,**args1):\n",
    "        if 99999999<args1['xh']<1000000000:\n",
    "            self.xh = args1['xh']\n",
    "        else:\n",
    "            raise ValueError('学号{}需要是9位数'.format(args1['xh']))\n",
    "            \n",
    "        self.xm = args1['xm']\n",
    "        self.ma = args1['ma']\n",
    "        self.bj = args1['bj']\n",
    "        self.xy = args1['xy']\n",
    "        self.yy = args1['yy']\n",
    "    def __repr__(self):\n",
    "        return \"HealthInfo(xh={},xm='{}',ma='{}',bj='{}',xy='{}',yy='{}')\".format(self.xh,self.xm,self.ma,self.bj,self.xy,self.yy)\n",
    "\n",
    "\n",
    "try:\n",
    "    stu0 = HealthInfo(xh=11111111,xm='haha',ma='红码',bj='111',xy='rrrr',yy='在国外')\n",
    "    print(stu0)\n",
    "except ValueError as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "学号11111111需要是9位数\n"
     ]
    }
   ],
   "source": [
    "#任务6.3.4自定义异常类 XH9Error,重新修改HealthInfo类，使用XH9Error类\n",
    "class XH9Error(Exception):pass\n",
    "\n",
    "class HealthInfo:\n",
    "    def __init__(self,*args0,**args1):\n",
    "        if 99999999<args1['xh']<1000000000:\n",
    "            self.xh = args1['xh']\n",
    "        else:\n",
    "            raise XH9Error('学号{}需要是9位数'.format(args1['xh']))\n",
    "            \n",
    "        self.xm = args1['xm']\n",
    "        self.ma = args1['ma']\n",
    "        self.bj = args1['bj']\n",
    "        self.xy = args1['xy']\n",
    "        self.yy = args1['yy']\n",
    "    def __repr__(self):\n",
    "        return \"HealthInfo(xh={},xm='{}',ma='{}',bj='{}',xy='{}',yy='{}')\".format(self.xh,self.xm,self.ma,self.bj,self.xy,self.yy)\n",
    "\n",
    "\n",
    "try:\n",
    "    stu0 = HealthInfo(xh=11111111,xm='haha',ma='红码',bj='111',xy='rrrr',yy='在国外')\n",
    "    print(stu0)\n",
    "except XH9Error as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(学号:11111111，姓名:haha，健康码:红码,班级:111,学院:rrrr,原因:在国外)\n",
      "[(学号:11111111，姓名:甲，健康码:红马,班级:光伏1902\n",
      "\n",
      ",学院:电子信息学院,原因:湖北旅居史), (学号:11111112，姓名:乙，健康码:皇马,班级:海军通信1803,学院:士官学院,原因:湖北旅居史), (学号:11111113，姓名:丙，健康码:绿马,班级:光伏1902,学院:电子信息学院,原因:湖北旅居史), (学号:11111114，姓名:丁，健康码:红马,班级:应用电子1801,学院:电子信息学院,原因:湖北旅居史), (学号:11111115，姓名:戊，健康码:皇马,班级:韩语1801,学院:电子信息学院,原因:湖北旅居史), (学号:11111116，姓名:己，健康码:绿马,班级:动画1701,学院:数码艺术学院,原因:湖北旅居史), (学号:11111117，姓名:庚，健康码:红马,班级:应用电子1801,学院:电子信息学院,原因:湖北旅居史), (学号:11111118，姓名:辛，健康码:皇马,班级:数媒技术1701,学院:数码艺术学院,原因:湖北旅居史), (学号:11111119，姓名:壬，健康码:绿马,班级:电气1701,学院:电子信息学院,原因:湖北旅居史), (学号:11111120，姓名:癸，健康码:红马,班级:计算机网络1701,学院:网络与通信学院,原因:湖北旅居史), (学号:11111121，姓名:甲，健康码:皇马,班级:电子信息1702,学院:电子信息学院,原因:湖北旅居史), (学号:11111122，姓名:乙，健康码:绿马,班级:光伏1901,学院:电子信息学院,原因:湖北旅居史), (学号:11111123，姓名:丙，健康码:红马,班级:韩语1801,学院:电子信息学院,原因:湖北旅居史), (学号:11111124，姓名:丁，健康码:皇马,班级:虚拟技术1901,学院:数码艺术学院,原因:湖北旅居史), (学号:11111125，姓名:戊，健康码:绿马,班级:电子商务1901,学院:数字商务学院,原因:湖北旅居史), (学号:11111126，姓名:己，健康码:红马,班级:电气1702,学院:电子信息学院,原因:湖北旅居史), (学号:11111127，姓名:庚，健康码:皇马,班级:视传1701,学院:数码艺术学院,原因:湖北旅居史), (学号:11111128，姓名:辛，健康码:绿马,班级:软件1903,学院:人工智能学院,原因:湖北旅居史), (学号:11111129，姓名:壬，健康码:红马,班级:机电1804,学院:智能制造学院,原因:湖北旅居史), (学号:11111130，姓名:癸，健康码:皇马,班级:应用电子1901,学院:电子信息学院,原因:湖北旅居史), (学号:11111131，姓名:甲，健康码:绿马,班级:电子质检1903,学院:电子信息学院,原因:湖北旅居史), (学号:11111132，姓名:乙，健康码:红马,班级:电子质检1902,学院:电子信息学院,原因:湖北旅居史), (学号:11111133，姓名:丙，健康码:皇马,班级:电气1701,学院:电子信息学院,原因:湖北旅居史), (学号:11111134，姓名:丁，健康码:绿马,班级:物联网1804,学院:网络与通信学院,原因:湖北旅居史), (学号:11111135，姓名:戊，健康码:红马,班级:数媒技术1701,学院:数码艺术学院,原因:湖北旅居史), (学号:11111136，姓名:己，健康码:皇马,班级:机电1902,学院:智能制造学院,原因:湖北旅居史), (学号:11111137，姓名:庚，健康码:绿马,班级:软件1705,学院:人工智能学院,原因:湖北旅居史), (学号:11111138，姓名:辛，健康码:红马,班级:模具1801,学院:智能制造学院,原因:湖北旅居史), (学号:11111139，姓名:壬，健康码:皇马,班级:物联网1702,学院:网络与通信学院,原因:湖北旅居史), (学号:11111140，姓名:癸，健康码:绿马,班级:移动应用1903,学院:人工智能学院,原因:湖北旅居史), (学号:11111141，姓名:甲，健康码:红马,班级:电子商务1901,学院:数字商务学院,原因:湖北旅居史), (学号:11111142，姓名:乙，健康码:皇马,班级:动画1803,学院:数码艺术学院,原因:湖北旅居史), (学号:11111143，姓名:丙，健康码:绿马,班级:环境1802,学院:电子信息学院,原因:湖北旅居史), (学号:11111144，姓名:丁，健康码:红马,班级:火箭计网1901,学院:士官学院,原因:湖北旅居史), (学号:11111145，姓名:戊，健康码:皇马,班级:数媒技术1902,学院:数码艺术学院,原因:湖北旅居史), (学号:11111146，姓名:己，健康码:绿马,班级:数控1702,学院:智能制造学院,原因:湖北旅居史), (学号:11111111，姓名:甲，健康码:红马,班级:光伏1902\n",
      "\n",
      ",学院:电子信息学院,原因:境外旅居史)]\n"
     ]
    }
   ],
   "source": [
    "#任务6.3.5 在HealthInfo类中编写静态函数从Excel读取数据，并生成 HealthInfo列表\n",
    "#使用第三方pandas库读取excel数据\n",
    "#使用try...except...else...finally...捕获和处理异常\n",
    "import pandas as pd\n",
    "\n",
    "class XH9Error(Exception):pass\n",
    "\n",
    "class HealthInfo:\n",
    "    def __init__(self,*args0,**args1):\n",
    "        if 9999999<args1['xh']<100000000:\n",
    "            self.xh = args1['xh']\n",
    "        else:\n",
    "            raise XH9Error('学号{}需要是8位数'.format(args1['xh']))\n",
    "            \n",
    "        self.xm = args1['xm']\n",
    "        self.ma = args1['ma']\n",
    "        self.bj = args1['bj']\n",
    "        self.xy = args1['xy']\n",
    "        self.yy = args1['yy']\n",
    "    def __repr__(self):\n",
    "        return \"HealthInfo(xh={},xm='{}',ma='{}',bj='{}',xy='{}',yy='{}')\".format(self.xh,self.xm,self.ma,self.bj,self.xy,self.yy)\n",
    "\n",
    "    @staticmethod\n",
    "    def getstudentfromexcel(filename):\n",
    "        xls = pd.ExcelFile(filename)\n",
    "        valuehb = xls.parse('湖北旅居史')\n",
    "        valuejw = xls.parse('境外人员')\n",
    "        studentshealthinfo = []\n",
    "        for i in range(valuehb.shape[0]):\n",
    "\n",
    "            try:\n",
    "                studentshealthinfo.append(HealthInfo(xh=int(valuehb.iloc[i,0]), xm=valuehb.iloc[i,1], xy=valuehb.iloc[i,3],bj=valuehb.iloc[i,4],ma=valuehb.iloc[i,5],yy='湖北旅居史'))\n",
    "            except Exception as e:\n",
    "                print(e)\n",
    "        for i in range(valuejw.shape[0]):\n",
    "\n",
    "            try:\n",
    "                studentshealthinfo.append(HealthInfo(xh=int(valuehb.iloc[i,0]), xm=valuehb.iloc[i,1], xy=valuehb.iloc[i,3],bj=valuehb.iloc[i,4],ma=valuehb.iloc[i,5],yy='境外旅居史'))\n",
    "            except Exception as e:\n",
    "                print(e)\n",
    "        return studentshealthinfo\n",
    "\n",
    "\n",
    "try:\n",
    "    stu0 = HealthInfo(xh=11111111,xm='haha',ma='红码',bj='111',xy='rrrr',yy='在国外')\n",
    "    print(stu0)\n",
    "except XH9Error as e:\n",
    "    print(e)\n",
    "\n",
    "\n",
    "try:\n",
    "    stus = HealthInfo.getstudentfromexcel('excel.xlsx')\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "else:\n",
    "    print(stus)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
